package visam.core;

/**
 * - transform to a linked list of gap buffers, ie the lines are buffers themselves.
 * - abstract the gap, coordinates should be absolute, regardless of where the gap is located.
 * - remove findNewLines, synch issues.
 */
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Rectangle;

public class Text {
    // private char[] buffer;
    private Buffer          buffer;
    private Vector<Integer> newlineMarkers;                               // Marks the new lines in the buffer with the gaps in order to draw them properly
                                                                           
    private boolean         colonmode             = false;
    private int             column                = 0;
    private int             line                  = 0;
    
    private char[]          clipboard             = null;                 // stow away the text to be pasted
    private boolean         lineInClipBoard;
    
    private Edit            undoList              = null;
    private Edit            redoList              = null;
    
    boolean                 visible               = false;                // determine the "blinking" of the cursor
                                                                           
    private static char[]   mcmds                 = new char[] {
                                                  'n', 'w', 'W', 'e', 'E', 'b', '.', '(', ')', '{', '}', '0', '$', 'h', 'j', 'k', 'l', 'H', 'M', 'L', 'u', 'G', 'n', 'N'
                                                  };
    private static char[]   xmcmds                = new char[] {
                                                  't', 'f'
                                                  };
    
    private static char[]   ecmds                 = new char[] {
                                                  'c', 'd', 'y', 'u'
                                                  };
    
    private static char[]   immediateEditCommands = new char[] {
                                                  'p', 'P', 'Y', 'D', 'u', 'x', 'X', 'o', 'O', 'A', 'I', 'J'
                                                  };
    
    private static char[]   ctrlChars             = new char[] {
                                                  'r', 'i', 'b', 'f'
                                                  };
    private int             dot;
    
    private Pattern         p;                                            // To compile the the regular expression
    private Matcher         m;                                            // To match the regular expression agains the buffer.
    private int             currentMatchPoint;                            // The current point which the regular expression
    private String          searchText;                                   // The search text that will comprise the regular expression
    private boolean         searching             = false;                // indicates that we are conducting a search and input handling of characters will behave accordingly
    private Vector<Integer> searchPoints          = new Vector<Integer>(); // The points that match the search
                                                                           
    public Text(int size) throws FileNotFoundException, IOException {
        buffer = new Buffer(size);
        buffer.setPoint(0);
    }
    
    public Text(String fname, int size) throws FileNotFoundException, IOException {
        buffer = new Buffer(fname, size);
        buffer.setPoint(0);
    }
    
    /**
     * Draw the contents of the buffer on the screen
     * 
     * @param gc
     *            - handle to the GC for drawing to the screen
     * @param rect
     *            - the dimensions available ror drawing
     */
    public void draw(GC gc, Rectangle rect) {
        buffer.draw(gc, rect);
    }
    
    /**
     * Insert character into the buffer and change the parameters associated with the gap accordingly.
     * Store the character written in the currenActionPoint so as to be able to undo when and if
     * the time comes that it is deemed necessary
     * 
     * @param c
     *            - character to be inserted in the buffer
     */
    public void insert(char c) {
        if (c == '\r') c = '\n';
        checkUndoList();
        if (undoList.pos + undoList.redotext.getSize() == buffer.getPoint()) {
            System.out.println("Text::insert: Addding " + c + " to current undo item");
            undoList.redotext.append(c);
        }
        else {
            System.out.println("Text::insert: Creating new undo record");
            createFinalUndoListItem();
            System.out.println("Text::insert: Addding " + c + " to current undo item");
            undoList.redotext.append(c);
        }
        System.out.print("current undo record");
        undoList.dump();
        buffer.insert(c);
    }
    
    /**
     * Delete the next character with regards to the point.
     * Store it in the undo list so that it can be regained later
     */
    public void deleteNext() {
        checkUndoList();
        if (undoList.pos + undoList.getRedoLen() != buffer.getPoint()) {
            createFinalUndoListItem();
        }
        buffer.deleteNext(1);
    }
    
    /**
     * Delete the previous character with regards to the point.
     * Store it in the undolist or the current Edit instance if it is deemed possible.
     */
    public void deletePrevious() {
        checkUndoList();
        if (undoList.pos + undoList.redotext.getSize() != buffer.getPoint()) {
            createFinalUndoListItem();
        }
        
        if (undoList.redotext.getSize() != 0) {
            undoList.redotext.delete(1);
        }
        else if (undoList.pos == buffer.getPoint()) {
            undoList.undotext.prepend(buffer.getChar(buffer.getPoint() - 1));
            undoList.pos = buffer.getPoint() - 1;
        }
        
        buffer.deletePrevious(1);
    }
    
    /**
     * Delete the previous character with regards to the point.
     * Store it in the undolist or the current Edit instance if it is deemed possible.
     */
    public void cutPrevious(int no) {
        if (buffer.getPoint() <= 0) return;
        checkUndoList();
        if (undoList.pos + undoList.redotext.getSize() != buffer.getPoint()) {
            createFinalUndoListItem();
        }
        
        int point = buffer.getPoint();
        clipboard = buffer.getText(point - no, point);
        
        if (undoList.redotext.getSize() - no >= 0) {
            undoList.redotext.delete(no);
        }
        else if (undoList.pos == buffer.getPoint()) {
            undoList.undotext.prepend(clipboard);
            undoList.pos = buffer.getPoint() - 1;
        }
        buffer.deletePrevious(no);
    }
    
    /**
     * Delete the next character with regards to the point.
     * Store it in the undo list so that it can be regained later
     */
    public void cutNext(int no) {
        checkUndoList();
        if (undoList.pos + undoList.getRedoLen() != buffer.getPoint()) {
            createFinalUndoListItem();
        }
        
        int point = buffer.getPoint();
        clipboard = buffer.getText(point, point + no);
        undoList.undotext.append(clipboard);
        buffer.deleteNext(no);
    }
    
    /**
     * Get the current column of the cursor
     * 
     * @return horizontal position, the column of the cursor
     */
    public int getColumn() {
        return buffer.getColumn();
    }
    
    /**
     * Get the current line of the cursor
     * 
     * @return vertical positon, the line of the cursor
     */
    public int getLine() {
        return buffer.getLine();
    }
    
    public void setColonMode() {
        buffer.setColonMode(true);
    }
    
    public void unsetColonMode() {
        buffer.setColonMode(false);
    }
    
    public boolean colonMode() {
        return buffer.colonMode();
    }
    
    public void saveAs(String fname) throws IOException, NullPointerException {
        buffer.saveAs(fname);
    }
    
    public void save() throws IOException {
        buffer.save();
    }
    
    public void open(String filename) throws IOException {
        buffer.open(filename);
    }
    
    public void append(String filename) {
        
    }
    
    char[]         movements = new char[] {
                             'h', 'j', 'k', 'l', 'n', 'N'
                             };
    private String regEx;
    
    private boolean movement(char c) {
        for (Character k : movements) {
            if (c == (char)k) return true;
        }
        return false;
    }
    
    public boolean process(Vector<KeyEvent> kbseq) {
        int limit = kbseq.size();
        int iterations = 0;
        char edit = 0;
        char nav = 0;
        char s = 0;
        
        for (int i = 0; i < limit; i++) {
            KeyEvent kevent = kbseq.elementAt(i);
            char c = kevent.character;
            
            if (c <= '9' && c >= '1') {
                iterations = iterations * 10 + (c - '0');
            }
            
            if (iterations > 0 && c == '0') {
                iterations *= 10;
                continue;
            }
            
            if (editcmd(c) && (kevent.stateMask & SWT.CONTROL) != SWT.CONTROL) {
                if (edit != 0 && edit == c) {
                    nav = c;
                    return act(edit, nav, iterations == 0 ? 1 : iterations, s);
                }
                else
                    edit = c;
            }
            
            if (ctrlcmd((char)kevent.keyCode) && (kevent.stateMask & SWT.CONTROL) == SWT.CONTROL) {
                return ctrl((char)kevent.keyCode);
            }
            
            if (immediateEditCommand(c)) {
                nav = c;
                return act(edit, nav, iterations == 0 ? 1 : iterations, s);
            }
            
            if (xmovementcmd(c)) {
                nav = c;
                s = kbseq.elementAt(i++).character;
                return act(edit, nav, iterations == 0 ? 1 : iterations, s);
            }
            
            if (movementcmd(c)) {
                nav = c;
                return act(edit, nav, iterations, s);
            }
        }
        return false;
    }
    
    /**
     * Check whether the editor is set in edit mode
     * 
     * @return true if the text buffer is set to edit mode, false if the editor is set to command mode.
     */
    public boolean editMode() {
        return buffer.editMode() == true;
    }
    
    /**
     * Check if the editor is set in command mode
     * 
     * @return true if the editor is set in command mode, false if the editor is set in eit mode
     */
    public boolean commandMode() {
        return buffer.editMode() == false;
    }
    
    /**
     * Set the editor in edit mode
     */
    public void setEditMode() {
        buffer.setEditMode(true);
    }
    
    /**
     * Set the editor in command mode
     */
    public void setCommandMode() {
        buffer.setEditMode(false);
    }
    
    private void storeTextInClipBoard(int start, int end) {
        clipboard = new char[end - start];
        char[] tmp = buffer.getBuffer();
        System.arraycopy(tmp, start, clipboard, 0, end - start);
        lineInClipBoard = false;
    }
    
    private void storeLinesInClipBoard(int start, int end) {
        clipboard = new char[end - start];
        char[] tmp = buffer.getBuffer();
        System.arraycopy(tmp, start, clipboard, 0, end - start);
        lineInClipBoard = true;
    }
    
    private void storeDeletedUndo(int start, int end) {
        createFinalUndoListItem();
        undoList.pos = start;
        undoList.undotext.append(buffer.getText(start, end));
    }
    
    private void storeDeletedUndo(int nextpos) {
        createFinalUndoListItem();
        int point = buffer.getPoint();
        
        if (nextpos < point) {
            undoList.pos = nextpos;
            undoList.undotext.append(buffer.getText(nextpos, point));
        }
        else if (nextpos > point) {
            undoList.pos = point;
            undoList.undotext.append(buffer.getText(point, nextpos));
        }
        
        System.out.print("current undo record");
        undoList.dump();
    }
    
    private boolean act(char edit, char nav, int iterations, char s) {
        System.out.println("act: [e:" + edit + ";n:" + nav + ";i:" + iterations + "]");
        boolean rc = true;
        if (nav != 'G' && iterations == 0) iterations = 1;
        switch (nav) {
            case 'G' :
                {
                    int i = 0;
                    int pos = 0;
                    final int limit = buffer.getBufferSize();
                    while (pos < limit && i < iterations) {
                        if (buffer.getChar(pos++) == '\n') i++;
                    }
                    buffer.setPoint(buffer.findBOL(pos));
                }
                break;
            case 'A' :
                {
                    int start = buffer.findBOL(buffer.getPoint());
                    int end = buffer.findEOL(start);
                    int nextpos = end;
                    char n = buffer.getChar(nextpos);
                    System.out.println("Text.act:Append: n==" + n + ":" + (int)n);
                    while (Character.isWhitespace(n) && nextpos >= start) {
                        n = buffer.getChar(--nextpos);
                        System.out.println("Text.act:Append: n==" + n + ":" + (int)n);
                    }
                    buffer.setPoint(nextpos + 1);
                    setEditMode();
                }
                break;
            case 'I' :
                {
                    int start = buffer.findBOL(buffer.getPoint());
                    int end = buffer.findEOL(start);
                    int nextpos = start;
                    char n = buffer.getChar(nextpos);
                    System.out.println("Text.act:Insert: n==" + n);
                    while (Character.isWhitespace(n) && nextpos >= end) {
                        n = buffer.getChar(++nextpos);
                        System.out.println("Text.act:Insert: n==" + n);
                    }
                    buffer.setPoint(nextpos);
                    setEditMode();
                }
                break;
            case 'o' :
                {
                    int pos = buffer.findEOL(buffer.getPoint());
                    createFinalUndoListItem();
                    buffer.replaceText(pos, pos, undoList.redotext.getArray());
                    undoList.pos = pos;
                    buffer.setPoint(pos + 1);
                    setEditMode();
                }
                break;
            case 'O' :
                {
                    int pos = buffer.findBOL(buffer.getPoint());
                    createFinalUndoListItem();
                    buffer.replaceText(pos, pos, undoList.redotext.getArray());
                    undoList.pos = pos;
                    buffer.setPoint(pos);
                    setEditMode();
                }
                break;
            case 'x' :
                {
                    cutNext(iterations);
                }
                break;
            case 'X' :
                {
                    cutPrevious(iterations);
                }
                break;
            case 'b' :
            case 'B' :
                {
                    int nextpos = buffer.getPoint();
                    for (int i = 0; i < iterations; i++) {
                        nextpos = buffer.reversedSkipOverWord(nextpos - 1, Buffer.TRAILING_EDGE);
                    }
                    if (edit != 0) {
                        if (nextpos <= 0) nextpos = 0;
                        modify(edit, nextpos, buffer.getPoint());
                        /*
                         * switch (edit)
                         * {
                         * case 'c' :
                         * setEditMode();
                         * case 'd' :
                         * storeDeletedUndo(nextpos);
                         * storeTextInClipBoard(nextpos, buffer.getPoint());
                         * buffer.extendGap(nextpos);
                         * break;
                         * case 'y' :
                         * storeTextInClipBoard(nextpos, buffer.getPoint());
                         * break;
                         * }
                         */
                    }
                    else {
                        buffer.setPoint(nextpos + 1);
                    }
                    
                }
                break;
            case 'W' :
            case 'w' :
                {
                    int nextpos = buffer.getPoint();
                    for (int i = 0; i < iterations; i++) {
                        nextpos = buffer.skipOverWord(nextpos + 1, Buffer.LEADING_EDGE);
                    }
                    
                    if (edit != 0) {
                        modify(edit, buffer.getPoint(), nextpos);
                    }
                    else {
                        buffer.setPoint(nextpos);
                    }
                    
                }
                break;
            case 'e' :
            case 'E' :
                {
                    int index = buffer.getPoint();
                    for (int i = 0; i < iterations; i++) {
                        index = buffer.skipOverWord(index + 1, Buffer.TRAILING_EDGE);
                    }
                    
                    if (edit != 0) {
                        modify(edit, buffer.getPoint(), index);
                    }
                    else
                        buffer.setPoint(index);
                    
                }
                break;
            case 't' :
                {
                    char[] remainder = buffer.getText(buffer.getPoint(), buffer.getBufferSize());
                    String tmp = new String(remainder, 0, remainder.length);
                    int index = 0;
                    
                    for (int i = 0; i < iterations; i++) {
                        index = tmp.indexOf(0x20, index + 1);
                    }
                    buffer.setPoint(buffer.getPoint() + index);
                }
                break;
            case 'T' :
                break;
            case 'f' :
                {
                    char[] remainder = buffer.getText(buffer.getPoint(), buffer.getBufferSize() - 1);
                    String tmp = new String(remainder, 0, remainder.length);
                    int index = 0;
                    
                    for (int i = 0; i < iterations; i++) {
                        index = tmp.indexOf(0x20, index + 1);
                    }
                    buffer.setPoint(buffer.getPoint() + index + 1);
                }
                break;
            case 'F' :
                break;
            case '.' :
                if (edit != 0) {
                    
                }
                break;
            case '(' :
            case '{' :
                {
                    int i = buffer.findPreviousEmptyLine();
                    if (i <= 0) i = 0;
                    if (edit != 0) {
                        modify(edit, i, buffer.getPoint());
                    }
                    buffer.setPoint(i > 0 ? i - 1 : i);
                }
                // find the next empty line backwards
                break;
            case ')' :
            case '}' :
                {
                    int i = findNextEmptyLine();
                    if (edit != 0) {
                        modify(edit, buffer.getPoint(), i);
                    }
                    else {
                        buffer.setPoint(i + 1);
                    }
                }
                // find the next empty line forwards
                break;
            case '0' :
                {
                    int bol = buffer.findBOL(buffer.getPoint());
                    if (edit != 0) {
                        modify(edit, bol, buffer.getPoint());
                    }
                    else {
                        buffer.setPoint(bol);
                    }
                    column = 0;
                }
                break;
            case '$' :
                {
                    if (edit != 0) {
                        int eol = buffer.findEOL(buffer.getPoint());
                        modify(edit, buffer.getPoint(), eol);
                    }
                    else {
                        int eol = buffer.findEOL(buffer.getPoint());
                        int bol = buffer.findBOL(buffer.getPoint());
                        column = eol - bol;
                        buffer.setPoint(eol);
                    }
                }
                break;
            case 'h' :
                buffer.moveCharLeft(iterations);
                break;
            case 'j' :
                buffer.moveLineDown(iterations);
                break;
            case 'k' :
                buffer.moveLineUp(iterations);
                break;
            case 'l' :
                buffer.moveCharRight(iterations);
                break;
            case 'H' :
                // TODO: Implement H command
                break;
            case 'M' :
                // TODO: Implement M command
                break;
            case 'L' :
                // TODO: Implement L command
                break;
            case 'u' :
                {
                    // Undo!
                    if (undoList != null) {
                        Edit item;
                        do {
                            item = undoList;
                            undoList = undoList.next;
                            item.next = null;
                            System.out.print("act:undo:item:");
                            item.dump();
                            // Fit the redotext in the same space that the undotext was put in.
                            buffer.replaceText(item.pos, item.getRedoEnd(), item.undotext.getArray());
                            item.next = redoList;
                            redoList = item;
                        }
                        while (!item.stop() && undoList != null);
                    }
                }
                break;
            case 'y' :
                {
                    int start = buffer.findBOL(buffer.getPoint());
                    int end = buffer.getPoint();
                    for (int i = 0; i < iterations; i++) {
                        end = buffer.findEOL(end + 1);
                    }
                    storeLinesInClipBoard(start, end + 1);
                }
                break;
            case 'Y' :
                act('y', '$', 1, (char)0);
                break;
            case 'd' :
                {
                    int nextpos = buffer.getPoint();
                    int start = buffer.findBOL(buffer.getPoint());
                    for (int i = 0; i < iterations; i++) {
                        nextpos = buffer.findEOL(nextpos + 1);
                    }
                    storeLinesInClipBoard(start, nextpos + 1);
                    buffer.setPoint(start);
                    storeDeletedUndo(nextpos);
                    buffer.extendGap(nextpos);
                }
                break;
            case 'D' :
                act('d', '$', 1, (char)0);
                break;
            case 'c' :
                {
                    int start = buffer.findBOL(buffer.getPoint());
                    int nextpos = buffer.getPoint();
                    for (int i = 0; i < iterations; i++) {
                        nextpos = buffer.findEOL(nextpos + 1);
                    }
                    storeLinesInClipBoard(start, nextpos);
                    buffer.setPoint(start);
                    storeDeletedUndo(nextpos);
                    buffer.extendGap(nextpos);
                    setEditMode();
                }
                break;
            case 'J' :
                {
                    // TODO: Implement J command
                    int i = buffer.findEOL(buffer.getPoint());
                    createFinalUndoListItem();
                    buffer.setPoint(i);
                    undoList.pos = i;
                    undoList.undotext.append(buffer.getChar(i));
                    buffer.deleteNext(1);
                }
                
                break;
            case 'p' :
                {
                    if (clipboard != null) {
                        int pos = lineInClipBoard ? buffer.findEOL(buffer.getPoint()) + 1 : buffer.getPoint() + 1;
                        buffer.replaceText(pos, pos, clipboard);
                        createFinalUndoListItem();
                        undoList.pos = pos;
                        undoList.redotext.set(clipboard);
                    }
                }
                break;
            case 'P' :
                {
                    if (clipboard != null) {
                        int pos = lineInClipBoard ? buffer.findBOL(buffer.getPoint()) : buffer.getPoint() + 1;
                        createFinalUndoListItem();
                        undoList.redotext.set(clipboard);
                        undoList.pos = pos;
                        buffer.replaceText(pos, pos, clipboard);
                        buffer.setPoint(buffer.getPoint() + clipboard.length);
                    }
                }
                break;
            case 'n' :
                do {
                    searchFwds();
                    iterations -= 1;
                }
                while (iterations > 0);
                break;
            case 'N' :
                do {
                    searchBkwds();
                    iterations -= 1;
                }
                while (iterations > 0);
                break;
            default :
                rc = false;
                break;
        }
        return rc;
    }
    
    public boolean ctrl(char c) {
        switch (c) {
            case 'r' :
                redo();
                break;
            case 'i' :
                break;
            case 'w' :
                {
                    int nextpos = buffer.reversedSkipOverWord(buffer.getPoint() - 1, Buffer.TRAILING_EDGE);
                    storeTextInClipBoard(nextpos, buffer.getPoint());
                    storeDeletedUndo(nextpos);
                    buffer.extendGap(nextpos);
                    buffer.setPoint(nextpos);
                }
                break;
            case 'u' :
                {
                    if (editMode()) {
                        int nextpos = buffer.findBOL(buffer.getPoint() - 1);
                        storeTextInClipBoard(nextpos, buffer.getPoint());
                        storeDeletedUndo(nextpos + 1);
                        buffer.extendGap(nextpos);
                        buffer.setPoint(nextpos);
                    }
                    else {
                        // traverse upwards in the text
                    }
                }
                break;
            case 'd' :
                {
                    
                }
                break;
            case 'f' :
                {
                    
                }
                break;
            case 'b' :
                // FIXME!!
                break;
            case 'y' :
                break;
            case 'e' :
                break;
            default :
                return false;
        }
        return true;
    }
    
    public void doRegExSearch(String re) {
        char[] buf = buffer.getBuffer();
        searchText = new String(buf, 0, buf.length).trim();
        System.out.println("re: (" + re + ")");
        p = Pattern.compile(re);
        m = p.matcher(searchText);
        if (m.find(buffer.getPoint())) {
            int pos = m.start();
            buffer.setPoint(pos);
        }
    }
    
    /**
     * Perform a forwards search of the text in the buffer to find
     * the next occurence of the
     * 
     * @return
     */
    public boolean searchFwds() {
        char[] b = buffer.getBuffer();
        searchText = new String(b, 0, b.length).trim();
        m = p.matcher(searchText);
        if (m.find(buffer.getPoint() + 1) || m.find()) {
            int pos = m.start();
            buffer.setPoint(pos);
            return true;
        }
        return false;
    }
    
    public boolean searchBkwds() {
        int end = buffer.getPoint();
        int start = buffer.findBOL(end);
        Vector<Integer> points = new Vector<Integer>();
        
        while (points.size() == 0 && start > 0) {
            char[] span = buffer.getBuffer(start, end);
            String string = new String(span, 0, span.length);
            
            m = p.matcher(string);
            while (m.find()) {
                int rstart = m.start();
                points.add((Integer)start + rstart);
            }
            
            end = start;
            start = buffer.findBOL(end - 1);
        }
        
        if (points.size() > 0) {
            int pos = points.lastElement();
            buffer.setPoint(pos);
            return true;
        }
        
        return false;
    }
    
    public void redo() {
        if (redoList != null) {
            Edit item;
            do {
                item = redoList;
                redoList = redoList.next;
                item.next = null;
                System.out.print("act:undo:item:");
                item.dump();
                buffer.replaceText(item.pos, item.getUndoEnd(), item.redotext.getArray());
                item.next = undoList;
                undoList = item;
            }
            while (!item.stop() && redoList != null);
        }
    }
    
    private static boolean movementcmd(char c) {
        final int limit = mcmds.length;
        for (int i = 0; i < limit; i++) {
            if (c == mcmds[i]) return true;
        }
        return false;
    }
    
    private boolean xmovementcmd(char c) {
        final int limit = xmcmds.length;
        for (int i = 0; i < limit; i++) {
            if (c == xmcmds[i]) {
                searching = false;
                return true;
            }
        }
        return false;
    }
    
    public boolean immediateEditCommand(char c) {
        final int limit = immediateEditCommands.length;
        for (int i = 0; i < limit; i++) {
            if (c == immediateEditCommands[i]) {
                searching = false;
                return true;
            }
        }
        return false;
    }
    
    public boolean ctrlcmd(char c) {
        final int limit = ctrlChars.length;
        for (int i = 0; i < limit; i++) {
            if (c == ctrlChars[i]) {
                searching = false;
                return true;
            }
        }
        return false;
    }
    
    public boolean editcmd(char c) {
        final int limit = ecmds.length;
        for (int i = 0; i < limit; i++) {
            if (c == ecmds[i]) {
                searching = false;
                return true;
            }
        }
        return false;
    }
    
    private int findNextEmptyLine() {
        int i = buffer.findEOL(buffer.getPoint());
        while (buffer.getChar(i + 1) != '\n') {
            i = buffer.findEOL(i);
        }
        return i;
    }
    
    private void createUndoListItem() {
        Edit item = new Edit();
        item.next = undoList;
        if (redoList != null) redoList = null;
        undoList = item;
        undoList.pos = buffer.getPoint();
    }
    
    private void createFinalUndoListItem() {
        createUndoListItem();
        undoList.setStop();
    }
    
    private void checkUndoList() {
        if (undoList == null) createFinalUndoListItem();
    }
    
    public String getFileName() {
        return buffer.getFileName();
    }
    
    public void modify(char edit, int start, int end) {
        switch (edit) {
            case 'c' :
                setEditMode();
            case 'd' :
                storeDeletedUndo(start, end);
                storeTextInClipBoard(start, end);
                buffer.extendGap(start, end);
                break;
            case 'y' :
                storeTextInClipBoard(start, end);
                break;
        }
        
    }
    
    public int getMaxLineCount() {
        return buffer.getMaxLineCount();
    }
    
    public void substitute(int start, int end, String pattern, String replacement, String flag) {
        int iterations = 0;
        if (flag == null) {
            iterations = 1;
        }
        else if (flag == "g") {
            iterations = -1;
        }
        
        else if (Character.isDigit(flag.charAt(0))) {
            for (int k = 0; k < flag.length(); k++) {
                iterations = iterations * 10 + (int)flag.charAt(k);
            }
        }
        
        for (int i = start; i < end; i++) {
            int a = newlineMarkers.elementAt(i - 1);
            int b = newlineMarkers.elementAt(i);
            String tmp = new String(buffer.getBuffer(a, b), 0, b - a);
            p = Pattern.compile(pattern);
            m = p.matcher(tmp);
            int index = 0;
            int count = 0;
            if (m.find(index)) {
                int f = a + m.start();
                int l = b + m.end();
                if (iterations == -1) {
                    buffer.replaceText(f, l, replacement.toCharArray());
                }
                else if (iterations == 1) {
                    buffer.replaceText(f, l, replacement.toCharArray());
                    continue;
                }
                else {
                    if (count == iterations) {
                        buffer.replaceText(f, l, replacement.toCharArray());
                        continue;
                    }
                    count++;
                }
                index = m.end();
            }
        }
    }
}
